#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import time
import socket

from htlib import *

class _ClientBannedException(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class SenseBarrier:
    """
    based on sense reversing barrier algorithm

    @message protocol 

    request:
        - barrier
        - cancel
    response code:
        - 200 OK
        - 300 Warning
        - 400 Client Error
        - 500 Server Error
    """
    def __init__(self, host, port, group_id=0, total_clients=1):
        self.server_addr   = (host, port)
        self.host          = host
        self.port          = port
        self.group_id      = group_id
        self.next_round    = 0
        self.total_clients = total_clients
        self.local_sense   = 1

        # timeout = 900
        # timeout = None
        # if timeout:
        #     print 'set socket default timeout %d!' % timeout 
        # socket.setdefaulttimeout(timeout)

    def barrier(self):
        self.next_round += 1
        self.local_sense = (self.local_sense + 1) % 2
        log_out('Group ID [%(gi)d] Total clients [%(tc)d] Request to enter barrier [%(nr)d]...\n' % {
            'gi': self.group_id, 
            'tc': self.total_clients, 
            'nr': self.next_round
            })

        send_msg = 'barrier#%(gi)d:%(nr)d:%(tc)d:%(ls)d' % { 
                'gi': self.group_id, 
                'nr': self.next_round, 
                'tc': self.total_clients, 
                'ls': self.local_sense 
                }
        try:
            conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            conn.setblocking(1)
            conn.connect(self.server_addr)
            conn.send(send_msg)
            recv_msg = conn.recv(1024)
        except socket.error, e:
            log_err('Connect to lockserver error: %s\n' % e)
            sys.exit(1)

        recv_msg = recv_msg.strip()

        log_out('recv msg: %s\n' % recv_msg)

        if recv_msg.startswith('300#'):
            try:
                real_clients = int(recv_msg[4:])
                log_err('Reset total_clients to %d\n' % real_clients)
                self.total_clients = real_clients
            except ValueError:
                pass
        elif recv_msg.startswith('400#'):
            raise _ClientBannedException(recv_msg)

        return recv_msg

    def cancel(self):
        self.next_round  = 0
        self.local_sense = 1

        send_msg = 'cancel#%d' % self.group_id
        conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        conn.setblocking(1)
        conn.connect(self.server_addr)
        conn.send(send_msg)
        recv_msg = conn.recv(1024)
        return recv_msg


###################################################
# Test lockserver section.
###################################################

def thread_main(args):
    total_client = options.clients
    group        = options.group
    lockserver   = options.lockserver
    this_barrier = SenseBarrier(lockserver, 20008, group, total_client)

    for st in xrange(0, 2):
        this_barrier.barrier()

        threadname = threading.currentThread().getName()
        threadname = threading.currentThread().getName()
        print 'stage %d: %s running....' % (st, threadname)
        time.sleep(2)

        this_barrier.barrier()
        print 'stage ****%d: %s running....' % (st, threadname)
        time.sleep(2)

        this_barrier.barrier()
        print 'stage ********%d: %s running....' % (st, threadname)
        time.sleep(2)
        this_barrier.barrier()
    
    this_barrier.cancel()
    print 'thread %s exit' % threadname

def main():
    threads = []

    for x in xrange(0, options.threads):
        threads.append(threading.Thread(target=thread_main, args=(1,)))

    for t in threads:
        t.start()

    for t in threads:
        t.join()

if __name__ == '__main__':
    from optparse import OptionParser 

    parser = OptionParser()
    parser.add_option("-g", "--group",  dest="group",    type='int', default="0")
    parser.add_option("-c", "--clients", dest="clients", type='int', default="2")
    parser.add_option("-t", "--threads", dest="threads", type='int', default="2")
    parser.add_option("-l", "--lockserver", dest="lockserver", type='string', default="127.0.0.1")
    (options, args) = parser.parse_args()
    main()
